#include "VibModelFileManager.h"
#include <iostream>
#include <iomanip>
#include <fstream>

#include "../util/Tokenizer.h"
#include "../geom/PolyModel.h"
#include "../geom/PolyObject.h"
#include <cmath>
using namespace dataengine;

bool VibModelFileManager::canLoadFile() throw()
{
	try
	{
		if(m_tok.get() == NULL)
			m_tok.reset(new util::Tokenizer(m_filename));
		m_tok->rewind();
		if(m_tok->getNextWord() != "VIBOSMODEL" || m_tok->getNextWord() != "VERSION" || m_tok->getNextWord() != "100")
			return false;
		return true;
	}
	catch(...)
	{
		return false;
	}
}

auto_ptr< geom::PolyObject > VibModelFileManager::loadFile() throw (DataException)
{
	auto_ptr< geom::PolyObject> result (new geom::PolyObject);
	if(m_tok.get() == NULL)
		m_tok.reset(new util::Tokenizer(m_filename));
	m_tok->rewind();
	m_tok->checkToken("VIBOSMODEL", m_tok->getNextWord());
	m_tok->checkToken("VERSION", m_tok->getNextWord());
	m_tok->checkToken("100", m_tok->getNextWord());

	auto_ptr<geom::PolyModel> currentModel;
	auto_ptr<geom::PolyMesh> currentMesh;

	string tokens[5]={"TREEMODEL","MESH","BASECOLOR","TRI","P"};	
	while(true)
	{
		string currentToken = m_tok->getNextWord();
		if(m_tok->eof())
			break;

		int tokenID=-1;
		for(int i=0;i<5;i++)
			if(currentToken == tokens[i])
				tokenID = i;
		
		if(tokenID == -1)
		{
			throw DataException("Token "+currentToken+" could not be interpreted as part of a model");
		}
		switch(tokenID)
		{
			case 0: {// TREEMODEL
						if(currentModel.get() != NULL)
						{
							if(currentMesh.get() !=NULL)
							{
								currentModel->addMesh(*(currentMesh.release()));
							}
							result->addModel(*currentModel);
						}
						currentModel.reset(new geom::PolyModel());
						currentModel->setName(m_tok->getNextWord());
					}break;
			case 1: {// MESH
						if(currentMesh.get() != NULL)
						{
							currentModel->addMesh(*currentMesh);
						}
						currentMesh.reset(new geom::PolyMesh());
						currentMesh->setName(m_tok->getNextWord());
					}break;
			case 2: {// BASECOLOR
						Triplet<float>color;
						for(int i=0;i<3;i++)
							color[i] = m_tok->getNextFloat();
						currentMesh->setBaseColor(color[0],color[1],color[2]);
					}break;
			case 3: {// TRI
						// We read the triangle 3 values
						Triplet<int> tri;
						tri[0] = (int) m_tok->getNextFloat();
						tri[1] = (int) m_tok->getNextFloat();
						tri[2] = (int) m_tok->getNextFloat();
						currentMesh->addTriangle(tri);
					}break;
			case 4: {// P(oint)
						// We read the point data
						util::Triplet<float> p;
						geom::MeshPoint po ;
						m_tok->readNextFloatTriplet(p);
						po.p = geom::Point3D(&(p[0]));

						m_tok->checkToken("N", m_tok->getNextWord());
						m_tok->readNextFloatTriplet(p);
						po.n = geom::Vector3D(&(p[0]));

						m_tok->checkToken("UV", m_tok->getNextWord());
						float u = m_tok->getNextFloat();
						float v = m_tok->getNextFloat();
						v = u-u+v; // para evitar warnings. Todavia no hacemos nada con las UV en ViBos
						m_tok->checkToken("AO", m_tok->getNextWord());
						po.AOFactor = m_tok->getNextFloat();
						// And add the point to the mesh
						currentMesh->addPoint(po); 
					}break;
			default:
					throw DataException("Found a nonenxistent token type!\n");
		}

	}
	if(currentMesh.get() !=NULL)
		currentModel->addMesh(*currentMesh);

	if(currentModel.get() !=NULL)
		result->addModel(*currentModel);

	return result;
}

float nanToZero(float value)
{
	if(isnan(value))
		return 0.0f;
	 return value;
}

void VibModelFileManager::saveToFile(const geom::PolyObject &ob) throw (DataException)
{
	ofstream saveFile(m_filename.c_str(),ios_base::trunc|ios_base::out);
	if(!saveFile)
		throw DataException("File\""+m_filename+"\" could not be opened for writing");
	saveFile<<"VIBOSMODEL VERSION 100"<<endl;
	for(int modelo=0;modelo<ob.getNumberOfModels();modelo++)
	{
		const geom::PolyModel & pmodel = ob.getModel(modelo);
		string name = pmodel.name();
		if(name == "")
			name = "_unnamed";
		saveFile<<"TREEMODEL "<<name<<endl;
		cout<<"Escribiendo modelo "<<pmodel.name()<<", que tiene "<<pmodel.numberOfMeshes()<<" mallas\n";
		for(int mesh=0; mesh<pmodel.numberOfMeshes();mesh++)
		{
			const geom::PolyMesh & pmesh = pmodel.getMesh(mesh);
			util::Triplet<float> color = pmesh.baseColor();

			string meshname = pmesh.name();
			if(meshname == "")
				meshname = "_unnamed";
			saveFile<<"MESH "<<meshname<<endl;

			saveFile<<"BASECOLOR "<< nanToZero(color[0])<<" "<<nanToZero(color[1])<<" "<<nanToZero(color[2])<<endl;
			cout<<"   Escribiendo malla"<<pmesh.name()<<", que tiene "<<pmesh.getNumberOfPoints()<<"puntos y "<<pmesh.getNumberOfTris()<<"tris\n";

			for(int point=0;point<pmesh.getNumberOfPoints(); point++)
			{
				const geom::MeshPoint m = pmesh.points()[point];
				const int floatWidth=12;
				saveFile<<"\tP "<<setw(floatWidth)<<nanToZero(m.p.x)<<" "<<setw(floatWidth)<<nanToZero(m.p.y)<<" "<<setw(floatWidth)<<nanToZero(m.p.z);
				saveFile<<"\tN "<<setw(floatWidth)<<nanToZero(m.n.dx)<<" "<<setw(floatWidth)<<nanToZero(m.n.dy)<<" "<<setw(floatWidth)<<nanToZero(m.n.dz);
				saveFile<<"\tUV 1.0 1.0";
				saveFile<<"\tAO "<<setw(floatWidth)<<nanToZero(m.AOFactor);
				saveFile<<endl;
			}
			saveFile<<endl;

			for(size_t tri=0; tri<pmesh.getNumberOfTris();tri++)
			{
				const Triplet<int> &meshtri = pmesh.triangles()[tri];
				const int intWidth = 5;
				saveFile << "\tTRI";
				for(int i=0;i<3;i++)
				{
					saveFile<<" "<<setw(intWidth)<<meshtri[i];
				}
				if(tri%4 == 3)
					saveFile<<endl;
				else
					saveFile<<" ";
			}
			saveFile<<endl;
		}
	}
	saveFile.close();
}
